bashを読もう!『オープンソースアプリケーションのアーキテクチャ』3.1〜3.3
こんにちは、虎塚です。
8月31日(日)に「オープンソースアプリケーションのアーキテクチャ読書会(1)」という勉強会に参加してきました。
とても面白い本だったので、3章の3.1〜3.2の内容をレポートしたいと思います。
はじめに
読書会の開催経緯
主催者の方がある経緯でSQLiteのソースコードを読もうとしたところ、想定したより巨大だったので、先にBarkeley DBを読むことにしたそうです。
Barkeley DBの資料を探す中で、この本『Architecture of Open Source Applications』に辿り着き、せっかくなので他にも面白そうな章を読んでみようということで、今回の読書会が企画されたとのことです。
読書会の進行
20人ほどの参加者を席順に従って2人ずつのペアに分け、それぞれに章の一部を割り当てました。30分ほどかけて読みながら内容をまとめ、その後で、順番に発表して行くという形式でした。実際にはペースを見ながら調整が行われ、読み込みと発表準備は15分延長されて、45分になりました。担当範囲は各ペア3〜4ページでした。
初回なので、ためしに上記の方法でということで行われましたが、とても上手くいったのではと感じました。
というわけで、内容をレポートします。
第3章 The Bourne-Again Shell
Chet Rameyさんが書かれた章です。Rameyさんは、bashの開発者兼メンテナをされている方だそうです。
3.1 導入
シェルとは何かという紹介でした。
vi風にiで挿入モードにしなければ入力ができないシェルがある、という話で盛り上がりました。
Bash
Bashの名前の由来は、Bourne-Again SHellの頭文字を取ったもの。、Bourne-Again SHellとは、「Stephen Bourneさんが作った元祖Bourne Shellに基づいて一から作った」という意味と、「生まれ変わらせた(born again)」という意味をかけているそうです。bashの作者は、Brian Foxさんです。(記述を修正しました。ご指摘ありがとうございます)。
bashは移植性が高く、ビルドするにはPOSIX環境があるだけでよい、という記述のくだりで、MicrosoftのServices for Unix(SFU)でもよい、とありました。しかし、参加者から、SFUはWindows 8で廃止されたとの情報が寄せられました。SFUとは、Microsoftの公式Cygwinのようなものだそうです(私は使ったことがありません)。
3.2 構文単位およびプリミティブ
プリミティブ
bashのトークンは次の3種類です。
- 予約語 (if, whileなど)
- 単語
- 演算子
変数およびパラメータ
おそらく次のことだと思われます。
- $@(引数ぜんぶ)
- $?(直前のexitの値)
パラメータは代入によって与えられます。パラメータが指定されても、何も文字を与えなければ、与えられません。つまり、「a=」のような指定は、空文字とみなされるそうです。ここで、unsetとみなされる、とありましたが、空文字との区別は分かりませんでした。(分かる方がいらっしゃいましたら、お教えください)
ドル記号の単語による参照と書き換え、という話題がありました。次のような動作のことでしょう。
$ c=Hello $ d="$c World" $ echo $d
ローカル変数とグルーバル変数について。
LANG=C foo
のようにしてfooコマンドを実行すると、ロケールの一時変更がその後に残りません。コマンドローカルとでもいえばよいでしょうか。
ちなみに、グローバル変数が標準なので、上記は後ろにコマンドをつけるとローカル変数になり、つけないとグローバル変数になります。
また、変数に対して、明示的に型を指定することができます。配列や整数を定義できます。
declare -a 配列 declare -i 整数
型を指定した場合と、しなかった場合の動作の違いを見てみます。
$ declare -i a $ a=1+1 $ b=1+1 $ echo $a 2 $ echo $b 1+1
上記では、echo $((b)) とすると、2が出力されます。$((1+1)) でも構いません。なお、配列の入れ子は作ることができません。
グローバルスコープ -> 一時スコープ -> ローカルスコープといったように、単位ごとにスコープが連結されているという話題も出てきます。
シェルプログラミング言語
bashはシェルプログラミング言語として十分な機能を持っている、という解説でした。
3.3 入力の処理
この節に入るにあたって、「bashのソースコードを見た方が理解が深まる」という注釈が参加者からありました。興味のある方は見てみてください。
- bashの入力読み込み
- 対話モード: ターミナル
- 非対話モード: スクリプトファイル
文字を受け取り、行単位に分け、行をパーサに渡してコマンドに変換します。
文字の受け取りには、readlineライブラリが使われています。
- コマンドラインの編集、入力内容の保存、過去コマンドの呼び出し、履歴展開
- キーシーケンスをreadlineコマンドにバインド
- コマンドを使ったユーザ定義マクロが使える
- Readlineの構造
- 読み込み/送出/実行/再表示
- 文字読み込み: read あるいは、マクロ
- 読み込んだ文字がキーマップのインデックスになる(8bitの1文字)
- 例)beginning-of-line: 行の開始位置に移動する
- 例)self-insert: 文字をバッファに入れる
- あるキーシーケンスの一部を他のコマンドにバインドする、という機能もある(具体例を探し中)
readlineの管理対象はCのcharだけです。マルチバイト文字をサポートするロケールでは、readlineが自動的に文字全体を読み込んでバッファに追加します。
キーシーケンスが編集コマンドに解決されたら、ターミナルを更新します。この時、次の3つのことを気にする必要があります。
1.画面に表示されている文字バッファの現在の状態 2.表示バッファの更新後の内容 3.表示されている文字
マルチバイト文字を扱う場合、1,2と3のバイト数が正確に一致するとは限りません。そこでreadlineは、1と2の差分を算出し、最適な表示方法を決定します。
アプリケーション側からのReadlineの拡張
readlineライブラリの動きをカスタマイズする方法が、2つ紹介されています。
- readlineの引数
- 実際、bashではそのようにして拡張されたコマンドが数十個もあります
- フック関数へのポインタに、既知の名前とインタフェースを使う
- アプリがreadlineの前に割り込むことができます
非インタラクティブな入力処理
readlineをシェルが使わない場合は、入力の受け取りにstdio等を使うことになりますが、非インタラクティブなモードであれば、bashのバッファ入力パッケージを使った方がよいとのことです。
マルチバイト文字対応
bashでは、ロケールを見てマルチバイトを使える環境かどうかを判断して、そうであった場合は入力をバイトバッファとして格納します。
readlineライブラリは、次のようなマルチバイト文字の扱い方を知っています。
- ある文字が画面でどれくらい場所をとるか
- バッファからの取り出しバイト数
- 前後移動する時にどこまで動くか
おわりに
というわけで、今日のレポートはここまでです。
じつは、今日の読書会では3章と4章の途中まで読んだのですが、ヘビーな内容だったこともあり、レポートは私の理解が追いついた3章の3.1〜3.3までです。続きを今度書くつもりです。
それでは、また。